
#include "Debug.h"
#include "Log.h"
#include "simple_protocol.h"
#include "utility.h"
#include <phNfcLib_Int.h>
#include <phNfcLib_Initialization.h>
#include <Reader/Reader.h>



enum {
	API_FELICA_TDX = 0,		
	API_FELICA_READWITHOUTENCRYPTION = 1,
	API_FELICA_WRITEWITHOUTENCRYPTION = 2,
	API_FELICA_REQUESTSYSTEMCODE = 3,
	API_FELICA_POLL = 4,
	API_FELICA_REQUESTSERVICE = 5,
	API_FELICA_APDU = 6,
	API_FELICA_NUM,
};

phStatus_t Felica_Poll(uint8_t* systemcode, uint8_t ** pData, uint16_t *pDataLen){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_PAL_FELICA);
	return phpalFelica_ReqC(psalfelica, systemcode, 0, pData, pDataLen);
}

phStatus_t Felica_Read(uint8_t     bNumServices, uint8_t* pServiceList, uint8_t bTxNumBlocks, uint8_t* pBlockList, uint8_t listlen, uint8_t* pRxNumBlocks,uint8_t* pBlockData){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_AL_FELICA);
	return phalFelica_Read(psalfelica, bNumServices, pServiceList, bTxNumBlocks, pBlockList, listlen, pRxNumBlocks, pBlockData);
}

phStatus_t Felica_Write(uint8_t     bNumServices, uint8_t* pServiceList, uint8_t bNumBlocks, uint8_t* pBlockList, uint8_t listlen, uint8_t* data){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_AL_FELICA);
	return phalFelica_Write(psalfelica, bNumServices, pServiceList, bNumBlocks, pBlockList, listlen, data);
}

phStatus_t Felica_Exchange(uint16_t wOption, uint16_t wN, uint8_t* pTxBuffer, uint16_t wTxLength, uint8_t ** ppRxBuffer, uint16_t * pRxLength){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_PAL_FELICA);
	return phpalFelica_Exchange(psalfelica, wOption, wN, pTxBuffer, wTxLength, ppRxBuffer, pRxLength);
}

phStatus_t Felica_APDU(uint8_t      timeout, uint8_t* pTxBuffer, uint16_t wTxLength, uint8_t ** ppRxBuffer, uint16_t * pRxLength){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_PAL_FELICA);
	return phpalFelica_APDU(psalfelica, timeout, pTxBuffer, wTxLength, ppRxBuffer, pRxLength);
}

phStatus_t Felica_APDU1( uint8_t* pTxBuffer, uint16_t wTxLength, uint8_t ** ppRxBuffer, uint16_t * pRxLength){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_AL_FELICA);
	return phalFelica_APDU(psalfelica, pTxBuffer, wTxLength, ppRxBuffer, pRxLength);
}

phStatus_t Felica_RequestService( uint8_t  bTxNumServices, uint8_t * pTxServiceList, uint8_t * pRxNumServices, uint8_t * pRxServiceList){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_AL_FELICA);
	return phalFelica_RequestService(psalfelica, bTxNumServices, pTxServiceList, pRxNumServices, pRxServiceList);
}

phStatus_t Felica_RequestSystemCode(uint8_t MaxNumberOfSystemCodes, uint8_t * pRxNumServices, uint8_t * pRxServiceList){
	void *psalfelica = phNfcLib_GetDataParams(PH_COMP_AL_FELICA);
	phStatus_t status;
	uint8_t rxnum = 0;
	uint8_t rxservice[256] = {0};

	status = phalFelica_RequestSystemCode(psalfelica, &rxnum, rxservice);
	if(status){
		return status;
	}

	if(MaxNumberOfSystemCodes == 0){		//demo 不允许上报，与Elatec 一致。
		return 1;
	}

	if(MaxNumberOfSystemCodes < rxnum)
	{
		rxnum = MaxNumberOfSystemCodes;
	}

	*pRxNumServices = rxnum;
	Mem_Reverse(rxservice, rxnum*2);
	memcpy(pRxServiceList, rxservice, rxnum*2);
	
	return 0;
}

/*--------------------------------------------------------------------------------------------------*/
/*
1.5.24.5. FeliCa_Poll
Command: [1D04][UInt16: SystemCode]
Response: [00][Bool: Result][Byte Array(8): IDm][Byte Array(8): PMm]
Example
Command: 1D04FFFF
(SystemCode: FFFF)
Response: 0001011603002D0CA50B03014B024F4993FF
(Result: true, IDm: 011603002D0CA50B, PMm: 03014B024F4993FF)
*/
static int api_Felica_Poll(buffer_t* command, buffer_t* response){
	uint16_t SystemCode = net_buf_simple_pull_be16(command);	
	uint8_t* rbuf = NULL;
	uint16_t rlen = 0;
	phStatus_t status = Felica_Poll((uint8_t *)&SystemCode, &rbuf, &rlen);
	uint8_t result = status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	LOG_B("\nrbuf", rbuf, rlen, 16);
	if(result){
		net_buf_simple_add_mem(response, rbuf, rlen);
	}

	return ERR_NONE;
}
/*--------------------------------------------------------------------------------------------------*/
/*
1.5.24.1. FeliCa_TDX
Command: [1D00][Byte Array(Var): TX][Byte: MaxRXByteCnt][Byte: MaximumResponseTime][Byte:
NumberOfBlocks]
Response: [00][Bool: Result][Byte Array(Var): RX]
Example
Command: 1D00060600FFFF0000FFFF04
(TX: 0600FFFF0000, MaxRXByteCnt: FF, MaximumResponseTime: FF, NumberOf-
Blocks: 04)
Response: 000112120101010701450F16000120220427674EFF
(Result: true, RX: 120101010701450F16000120220427674EFF)
*/
typedef struct {
	uint8_t ByteCnt;
	uint8_t *Data;
	uint8_t MaxRXByteCnt;
	uint8_t MaximumResponseTime;
	uint8_t NumberOfBlocks;
}Felica_TDX_param_t;

static void get_command_param_Felicas_TDX(buffer_t* command, Felica_TDX_param_t* param){
	param->ByteCnt = net_buf_simple_pull_u8(command);
	param->Data = net_buf_simple_pull_mem(command, param->ByteCnt);
	param->MaxRXByteCnt = net_buf_simple_pull_u8(command);
	param->MaximumResponseTime = net_buf_simple_pull_u8(command);
	param->NumberOfBlocks = net_buf_simple_pull_u8(command);
}

static int api_Felica_TDX(buffer_t* command, buffer_t* response){
	Felica_TDX_param_t param;	
	get_command_param_Felicas_TDX(command, &param);
	LOG_D("\nMaxRXByteCnt=%d, MaximumResponseTime=%d, NumberOfBlocks=%d", param.MaxRXByteCnt, param.MaximumResponseTime, param.NumberOfBlocks);
	LOG_B("\nData", param.Data, param.ByteCnt, 16);
	uint8_t* rbuf = NULL;
	uint16_t rlen = 0;
	phStatus_t Status = Felica_APDU(param.MaximumResponseTime, param.Data, param.ByteCnt, &rbuf, &rlen);
	uint8_t result = Status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	LOG_B("\nrbuf", rbuf, rlen, 16);
	if(result){
		if(rlen > param.MaxRXByteCnt){
			rlen = param.MaxRXByteCnt;
		}
		net_buf_simple_add_u8(response, rlen);
		net_buf_simple_add_mem(response, rbuf, rlen);
	}
	return ERR_NONE;
}

typedef struct{
	uint8_t bNumServices;
	uint8_t *ServiceCodeList;
	uint8_t bTxNumBlocks;
	uint8_t *BlockList;
}read_felica_param_t;


static void get_command_param_readfelica(buffer_t* command, read_felica_param_t* param){
	param->bNumServices = net_buf_simple_pull_u8(command);
	param->ServiceCodeList = net_buf_simple_pull_mem(command, param->bNumServices * 2);
	param->bTxNumBlocks = net_buf_simple_pull_u8(command);
	param->BlockList = net_buf_simple_pull_mem(command, param->bTxNumBlocks * 2);
}

/*---------------------------------------------------------------------------*/
/*
1.5.24.2. FeliCa_ReadWithoutEncryption
Command: [1D01][variable number of UInt16: ServiceCodeList][variable number of UInt16: Block-
List]
Response: [00][Bool: Result][Byte Array(Var), 2 LB: Data]
Example
Command: 1D01010B10010000
(ServiceCodeList: 100B, BlockList: 0000)
Response: 0001100000000000000000000000000000000000
(Result: true, Data: 00000000000000000000000000000000)
*/
static int api_Felica_Read(buffer_t* command, buffer_t* response){

	read_felica_param_t param;
	get_command_param_readfelica(command, &param);
	uint8_t rnum = 0;
	uint8_t data[256] = {0};
	uint8_t  aBlockList[256] = {0};
	uint16_t Blocklistvalue;
	uint8_t index;
	
	LOG_D("\nbNumServices=%d, bTxNumBlocks=%d,", param.bNumServices, param.bTxNumBlocks);
	LOG_SBUF("ServiceCodeList data", param.ServiceCodeList, param.bNumServices * 2);
	LOG_SBUF("BlockList data", param.BlockList, param.bTxNumBlocks * 2);
	memset(aBlockList, 0, sizeof(aBlockList));

	index = 0;
	for(int i=0; i<param.bTxNumBlocks; i++){
		memcpy(&Blocklistvalue, &param.BlockList[i*2], 2);
		LOG_D("Blocklistvalue=%d", Blocklistvalue);
		if(Blocklistvalue <= 0xFFU)
		{
			/* 2 byte format */
			aBlockList[index++] = 0x80;
			aBlockList[index++] = (uint8_t)Blocklistvalue;
		}
		else
		{
			/* 3 byte format */
			aBlockList[index++] = 0x00;
			aBlockList[index++] = (uint8_t)Blocklistvalue;
			aBlockList[index++] = (uint8_t)(Blocklistvalue >> 8U);
		}
	}

	phStatus_t status = Felica_Read(param.bNumServices, param.ServiceCodeList, param.bTxNumBlocks, aBlockList, index, &rnum, data);	
	uint8_t result = status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	if(result){		
		uint16_t dlen = rnum * 16;
		LOG_D("\nrNum=%d", rnum);
		LOG_SBUF("data", data, dlen);
		net_buf_simple_add_le16(response, dlen);
		net_buf_simple_add_mem(response, data, dlen);		
	}
	return ERR_NONE;
}

typedef struct {
	uint8_t bNumServices;
	uint8_t *ServiceCodeList;
	uint8_t bTxNumBlocks;
	uint8_t *BlockList;
	uint16_t datalen;
	uint8_t *data;
}write_felica_param_t;

static void get_command_param_Writefelica(buffer_t* command, write_felica_param_t* param){
	param->bNumServices = net_buf_simple_pull_u8(command);
	param->ServiceCodeList = net_buf_simple_pull_mem(command, param->bNumServices * 2);
	param->bTxNumBlocks = net_buf_simple_pull_u8(command);
	param->BlockList = net_buf_simple_pull_mem(command, param->bTxNumBlocks * 2);
	param->datalen = net_buf_simple_pull_be16(command);
	param->data = net_buf_simple_pull_mem(command, param->datalen);
}
/*---------------------------------------------------------------------------*/
/*
1.5.24.3. FeliCa_WriteWithoutEncryption
Command: [1D02][variable number of UInt16: ServiceCodeList][variable number of UInt16: Block-
List][Byte Array(Var), 2 LB: Data]
Response: [00][Bool: Result]
Example
Command: 1D02010910010000100000000000000000000000000000000000
(ServiceCodeList: 1009, BlockList: 0000, Data: 00000000000000000000000000000000)
Response: 0001
(Result: true)
*/
static int api_Felica_Write(buffer_t* command, buffer_t* response){
	write_felica_param_t param;
	get_command_param_Writefelica(command, &param);
	uint8_t  aBlockList[256] = {0};
	uint16_t Blocklistvalue;
	uint8_t index;

	LOG_D("\nbNumServices=%d, bTxNumBlocks=%d,", param.bNumServices, param.bTxNumBlocks);
	LOG_SBUF("ServiceCodeList data", param.ServiceCodeList, param.bNumServices * 2);
	LOG_SBUF("BlockList data", param.BlockList, param.bTxNumBlocks * 2);
	LOG_D("write data len =%d", param.datalen);
	LOG_SBUF("write data", param.data, param.datalen);
	memset(aBlockList, 0, sizeof(aBlockList));

	index = 0;
	for(int i=0; i<param.bTxNumBlocks; i++){
		memcpy(&Blocklistvalue, &param.BlockList[i*2], 2);
		LOG_D("Blocklistvalue=%d", Blocklistvalue);
		if(Blocklistvalue <= 0xFFU)
		{
			/* 2 byte format */
			aBlockList[index++] = 0x80;
			aBlockList[index++] = (uint8_t)Blocklistvalue;
		}
		else
		{
			/* 3 byte format */
			aBlockList[index++] = 0x00;
			aBlockList[index++] = (uint8_t)Blocklistvalue;
			aBlockList[index++] = (uint8_t)(Blocklistvalue >> 8U);
		}
	}

	phStatus_t status = Felica_Write(param.bNumServices, param.ServiceCodeList, param.bTxNumBlocks, aBlockList, index, param.data);	
	uint8_t result = status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	return ERR_NONE;
}

/*---------------------------------------------------------------------------*/
/*
1.5.24.4. FeliCa_RequestSystemCode
Command: [1D03][Byte: MaxNumberOfSystemCodes]
Response: [00][Bool: Result][variable number of UInt16: SystemCodeList]
Example
Command: 1D0308
(MaxNumberOfSystemCodes: 08)
Response: 000103030000FEA786
(Result: true, SystemCodeList: 0003, FE00, 86A7)
*/
static int api_Felica_RequestSystemCode(buffer_t* command, buffer_t* response){
	uint8_t MaxNumberOfSystemCodes = net_buf_simple_pull_u8(command);
	uint8_t rbuf[256] = {0};
	uint8_t pRxNum = 0;
	phStatus_t status = Felica_RequestSystemCode(MaxNumberOfSystemCodes, &pRxNum, rbuf);	
	uint8_t result = status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	if(result){		
		net_buf_simple_add_u8(response, pRxNum);
		net_buf_simple_add_mem(response, rbuf, pRxNum*2);
	}
	return ERR_NONE;
}


typedef struct {
	uint8_t bTxNumServices;
	uint8_t *ServiceCodeList;
}Request_felica_param_t;

static void get_command_param_RequestService(buffer_t* command, Request_felica_param_t* param){
	param->bTxNumServices = net_buf_simple_pull_u8(command);
	param->ServiceCodeList = net_buf_simple_pull_mem(command, param->bTxNumServices * 2);
}
/*---------------------------------------------------------------------------*/
/*
1.5.24.6. FeliCa_RequestService
Command: [1D05][variable number of UInt16: ServiceCodeList]
Response: [00][Bool: Result][variable number of UInt16: KeyVersionList]
Example
Command: 1D05010000
(ServiceCodeList: 0000)
Response: 0001010100
(Result: true, KeyVersionList: 0001)
*/
static int api_Felica_RequestService(buffer_t* command, buffer_t* response){
	Request_felica_param_t param;
	get_command_param_RequestService(command, &param);
	uint8_t rbuf[256] = {0};
	uint8_t pRxNum = 0;

	LOG_D("\nbTxNumServices=%d", param.bTxNumServices);
	LOG_SBUF("ServiceCodeList data", param.ServiceCodeList, param.bTxNumServices * 2);

	phStatus_t status = Felica_RequestService(param.bTxNumServices, param.ServiceCodeList, &pRxNum, rbuf);	
	uint8_t result = status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	if(result){
		net_buf_simple_add_u8(response, pRxNum);
		net_buf_simple_add_mem(response, rbuf, pRxNum*2);
	}
	return ERR_NONE;
}

/*--------------------------------------------------------------------------------------------------*/
/*
1.5.24.1. FeliCa_APDU
Command: [1D06][Byte Array(Var): TX]
Response: [00][Bool: Result][Byte Array(Var): RX]
Example
*/
typedef struct {
	uint8_t ByteCnt;
	uint8_t *Data;
}Felica_APDU_param_t;

static void get_command_param_Felica_APDU(buffer_t* command, Felica_APDU_param_t* param){
	param->ByteCnt = net_buf_simple_pull_u8(command);
	param->Data = net_buf_simple_pull_mem(command, param->ByteCnt);
}

static int api_Felica_APDU(buffer_t* command, buffer_t* response){
	Felica_APDU_param_t param;	
	get_command_param_Felica_APDU(command, &param);
	LOG_D("\nByteCnt=%d",  param.ByteCnt);
	LOG_B("\nData", param.Data, param.ByteCnt, 16);
	uint8_t* rbuf = NULL;
	uint16_t rlen = 0;
	phStatus_t Status = Felica_APDU1(param.Data, param.ByteCnt, &rbuf, &rlen);
	uint8_t result = Status ? 0 : 1;	
	net_buf_simple_push_u8(response, result);
	LOG_B("\nrbuf", rbuf, rlen, 16);
	if(result){
		net_buf_simple_add_u8(response, rlen);
		net_buf_simple_add_mem(response, rbuf, rlen);
	}
	return ERR_NONE;
}


static const api_entry_t api_felica_list[API_FELICA_NUM] = {
	[API_FELICA_TDX] = api_Felica_TDX,
	[API_FELICA_READWITHOUTENCRYPTION] = api_Felica_Read,
	[API_FELICA_WRITEWITHOUTENCRYPTION] = api_Felica_Write,
	[API_FELICA_REQUESTSYSTEMCODE] = api_Felica_RequestSystemCode,
	[API_FELICA_POLL] = api_Felica_Poll,
	[API_FELICA_REQUESTSERVICE] = api_Felica_RequestService,
	[API_FELICA_APDU] = api_Felica_APDU,
};
	
static int Felica_api(buffer_t* command, buffer_t* response){
	const uint8_t num = sizeof(api_felica_list)/sizeof(api_felica_list[0]);
	return protocol_call_api(api_felica_list, num, command, response);
}

SIMPLE_API_DEFINE(API_CLASS_FELICA, Felica_api);



